package org.tloss.web.editor.form.editors;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeColumn;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.part.EditorPart;
import org.tloss.web.editor.Utils;
import org.tloss.web.editor.database.editors.jdbc.Table;
import org.tloss.web.editor.form.editors.command.AbstractCommandElement;
import org.tloss.web.editor.form.editors.command.BodyCommandElement;
import org.tloss.web.editor.form.editors.command.CommandRootElement;
import org.tloss.web.editor.form.editors.command.LoadEnityElement;
import org.tloss.web.editor.form.editors.command.MenuFactory;
import org.tloss.web.editor.form.editors.command.MenuItem;
import org.tloss.web.editor.model.Form;
import org.tloss.web.editor.model.Sql;
import org.tloss.web.editor.model.command.Command;
import org.tloss.web.editor.model.command.LoadEnity;
import org.tloss.web.editor.model.variable.EntityCommandVariable;
import org.tloss.web.editor.utils.VariableProvider;

public class CommandEditor extends EditorPart implements
		IResourceChangeListener, VariableProvider {
	private Form form = null;
	CheckboxTreeViewer treeViewer;
	IResourceChangeListener dirtyListenter;
	BodyCommandElement commandElement;
	BodyProvider provider;

	public void setDirtyListenter(IResourceChangeListener dirtyListenter) {
		this.dirtyListenter = dirtyListenter;
	}

	public void setForm(Form form, BodyProvider provider) {
		this.form = form;
		this.provider = provider;
		if (commandElement == null) {
			commandElement = new BodyCommandElement(provider, this);
		} else {
			commandElement.setForm(provider);
		}
		if (treeViewer != null) {
			treeViewer.refresh();
		}
	}

	/*
	 * public Form getForm() { return form; }
	 */

	@Override
	public void doSave(IProgressMonitor monitor) {

	}

	@Override
	public void doSaveAs() {

	}

	@Override
	public void init(IEditorSite site, IEditorInput input)
			throws PartInitException {
		if (!(input instanceof IFileEditorInput)) {
			PartInitException exception = new PartInitException(
					"Input must be a file");
			Utils.logError("Input must be a file", exception);
			throw exception;
		}
		setSite(site);
		setInput(input);
	}

	@Override
	public boolean isDirty() {
		return false;
	}

	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}

	MenuFactory factory = new MenuFactory();

	protected void initInsertInto(IMenuManager manager,
			final AbstractCommandElement commandElement) {
		MenuManager menuManager = new MenuManager("Insert into");
		final List<MenuItem> items = factory.initInsertInto(menuManager,
				commandElement, CommandEditor.this);
		if (items != null) {
			Action action;
			for (int i = 0; i < items.size(); i++) {
				final AbstractCommandElement element = items.get(i)
						.getElement();
				action = new Action() {
					public void run() {
						(commandElement).add(element);
						treeViewer.expandToLevel((commandElement), 1);
						treeModified(commandElement);
					}
				};
				action.setText(items.get(i).getText());
				menuManager.add(action);
			}
		}

		manager.add(menuManager);
	}

	protected void initNext(IMenuManager manager,
			final AbstractCommandElement commandElement) {
		MenuManager menuManager = new MenuManager("Insert next");

		final List<MenuItem> items = factory.initNext(menuManager,
				commandElement, CommandEditor.this);
		if (items != null) {
			Action action;
			for (int i = 0; i < items.size(); i++) {
				final AbstractCommandElement element = items.get(i)
						.getElement();
				action = new Action() {
					public void run() {
						Object parent = commandElement.getParent(null);
						if (parent != null
								&& parent instanceof AbstractCommandElement) {
							((AbstractCommandElement) commandElement
									.getParent(null)).add(element);
							treeViewer.expandToLevel((parent), 1);
							treeModified(parent);
						}
					}
				};
				action.setText(items.get(i).getText());
				menuManager.add(action);
			}
		}
		manager.add(menuManager);
	}

	private void initSimpleContextMenu() {

		MenuManager menuMgr = new MenuManager();
		menuMgr.addMenuListener(new IMenuListener() {

			@Override
			public void menuAboutToShow(IMenuManager manager) {
				TreeItem[] items = treeViewer.getTree().getSelection();

				if (items != null && items.length > 0) {
					final AbstractCommandElement commandElement;
					if (items[0].getData() instanceof AbstractCommandElement) {
						commandElement = (AbstractCommandElement) items[0]
								.getData();
						if (commandElement.isSupportedChildren()) {
							initInsertInto(manager, commandElement);
							if (commandElement.getParent(null) != null
									&& commandElement.getParent(null) instanceof AbstractCommandElement)
								initNext(manager, commandElement);
						} else {
							initNext(manager, commandElement);
						}
					}
				}

			}
		});
		menuMgr.setRemoveAllWhenShown(true);
		Menu menu = menuMgr.createContextMenu(treeViewer.getTree());

		treeViewer.getTree().setMenu(menu);
	}

	@Override
	public void createPartControl(final Composite parent) {
		Composite composite = new Composite(parent, SWT.NONE);
		GridLayout layout = new GridLayout();
		composite.setLayout(layout);
		layout.numColumns = 2;

		Composite treeContainer = new Composite(composite, SWT.BORDER);
		GridData gridData = new GridData();
		treeContainer.setLayoutData(gridData);
		gridData.horizontalAlignment = SWT.FILL;
		gridData.grabExcessHorizontalSpace = true;
		gridData.verticalAlignment = SWT.FILL;
		gridData.grabExcessVerticalSpace = true;
		TreeColumnLayout treeLayout = new TreeColumnLayout();
		treeContainer.setLayout(treeLayout);
		treeViewer = new CheckboxTreeViewer(treeContainer, SWT.MULTI
				| SWT.FULL_SELECTION);
		Tree tree = treeViewer.getTree();
		tree.setHeaderVisible(true);
		TreeColumn keyColumn;

		keyColumn = new TreeColumn(tree, SWT.NONE);
		keyColumn.setText("Name");
		treeLayout.setColumnData(keyColumn, new ColumnWeightData(2));

		treeViewer.setContentProvider(new WorkbenchContentProvider());
		final WorkbenchLabelProvider treeLabelProvider = new WorkbenchLabelProvider();
		treeViewer.setLabelProvider(treeLabelProvider);

		TreeViewerColumn column1 = new TreeViewerColumn(treeViewer, keyColumn);

		column1.setLabelProvider(new ColumnLabelProvider() {
			public String getText(Object element) {
				return treeLabelProvider.getText(element);
			}

			public Image getImage(Object element) {

				return treeLabelProvider.getImage(element);
			}
		});

		getSite().setSelectionProvider(treeViewer);
		if (commandElement == null) {
			commandElement = new BodyCommandElement(provider, this);
		} else {
			commandElement.setForm(provider);
		}
		CommandRootElement commandRootElement = new CommandRootElement(
				commandElement);
		treeViewer.setInput(commandRootElement);

		treeViewer.setAutoExpandLevel(TreeViewer.ALL_LEVELS);
		treeViewer.addSelectionChangedListener(new ISelectionChangedListener() {
			public void selectionChanged(SelectionChangedEvent event) {
				// if the selection is empty clear the label
				if (event.getSelection().isEmpty()) {
					return;
				}
				if (event.getSelection() instanceof IStructuredSelection) {
					IStructuredSelection selection = (IStructuredSelection) event
							.getSelection();
					for (Iterator<?> iterator = selection.iterator(); iterator
							.hasNext();) {
						AbstractCommandElement domain = (AbstractCommandElement) iterator
								.next();
						currentElement = domain;
					}
				}
			}
		});

		initSimpleContextMenu();

		Composite wrapper = new Composite(composite, SWT.NONE);
		RowLayout rowLayout = new RowLayout();
		wrapper.setLayout(rowLayout);
		rowLayout.type = SWT.VERTICAL;

		Button buttonDelete = new Button(wrapper, SWT.NONE);
		buttonDelete.setText("Delete");

		buttonDelete.addMouseListener(new MouseListener() {

			@Override
			public void mouseUp(MouseEvent e) {

			}

			@Override
			public void mouseDown(MouseEvent e) {
				Object[] items = treeViewer.getCheckedElements();
				if (items != null && items.length > 0) {
					for (int i = 0; i < items.length; i++) {
						if (items[i] != null
								&& items[i] instanceof AbstractCommandElement) {
							if (((AbstractCommandElement) items[i])
									.getParent(null) != null
									&& ((AbstractCommandElement) items[i])
											.getParent(null) instanceof AbstractCommandElement) {
								((AbstractCommandElement) ((AbstractCommandElement) items[i])
										.getParent(null))
										.remove((AbstractCommandElement) items[i]);
							}
						}
					}
					treeModified(null);
				}

			}

			@Override
			public void mouseDoubleClick(MouseEvent e) {

			}
		});
		Button button = new Button(wrapper, SWT.NONE);
		button.setText("Up");
		button.addMouseListener(new MouseListener() {

			@Override
			public void mouseUp(MouseEvent e) {

			}

			@Override
			public void mouseDown(MouseEvent e) {
				Object[] items = treeViewer.getCheckedElements();
				if (items != null && items.length > 0) {
					if (items[0] != null
							&& items[0] instanceof AbstractCommandElement) {
						if (((AbstractCommandElement) items[0]).getParent(null) != null
								&& ((AbstractCommandElement) items[0])
										.getParent(null) instanceof AbstractCommandElement) {
							((AbstractCommandElement) ((AbstractCommandElement) items[0])
									.getParent(null))
									.up((AbstractCommandElement) items[0]);
						}
					}
					treeModified(null);
				}
			}

			@Override
			public void mouseDoubleClick(MouseEvent e) {

			}
		});
	}

	@Override
	public void setFocus() {

	}

	public void treeModified(Object element) {
		if (element == null)
			treeViewer.refresh();
		else
			treeViewer.refresh(element);

		if (dirtyListenter != null) {
			dirtyListenter.resourceChanged(null);
		}
	}

	private List<Object> variables = new ArrayList<Object>();
	private AbstractCommandElement currentElement = null;

	@Override
	public void resourceChanged(IResourceChangeEvent arg0) {
		treeModified(null);
	}

	public Command getCurrentCommand() {
		if (currentElement != null)
			return currentElement.getCommand();
		return null;
	}

	public List<Object> getCurrentCommandVariables() {
		return getCurrentCommandVariables(VariableProvider.ALL);
	}

	public List<Object> getCurrentCommandVariables(int type) {

		return getCommandVariables(getCurrentCommand(), type);
	}

	protected void processVariable(AbstractCommandElement element) {
		if (element instanceof LoadEnityElement) {
			LoadEnity enity = (LoadEnity) ((LoadEnityElement) element)
					.getCommand();
			if (enity != null) {
				Sql sql = Utils.loadSql(enity.getSqlResource());
				if (sql != null) {
					Table table = Utils.loadEnity(Utils.getCurrentProject(),
							sql.getTables().get(0).getTableName());
					EntityCommandVariable commandVariable = new EntityCommandVariable();
					commandVariable.setName(enity.getVariableName());
					commandVariable.setTable(table);
					variables.add(commandVariable);
				}
			}
		}
	}

	protected void browseCommand(AbstractCommandElement root,
			ArrayList<AbstractCommandElement> parents) {
		Object[] children = root.getChildren(null);
		boolean stop = false;
		for (int i = 0; !stop && children != null && parents.size() > 0
				&& i < children.length; i++) {
			if (children[i] != null
					&& children[i] instanceof AbstractCommandElement) {
				if (((AbstractCommandElement) children[i]).getCommand().equals(
						parents.get(0).getCommand())) {
					parents.remove(0);
					processVariable(((AbstractCommandElement) children[i]));
					browseCommand(((AbstractCommandElement) children[i]),
							parents);
					stop = true;
				} else {
					processVariable(((AbstractCommandElement) children[i]));
				}
			}
		}
	}

	public List<Object> getCommandVariables(Command c) {
		variables.clear();
		variables.add(form);

		return variables;
	}

	public List<Object> getCommandVariables(Command c, int type) {
		variables.clear();
		variables.add(form);
		if (currentElement != null) {
			ArrayList<AbstractCommandElement> parents = new ArrayList<AbstractCommandElement>();
			AbstractCommandElement tmp = currentElement;
			Object obj;
			parents.add(tmp);
			while (tmp != null && (obj = tmp.getParent(null)) != null) {
				if (obj instanceof AbstractCommandElement) {
					parents.add(0, (AbstractCommandElement) obj);
					tmp = (AbstractCommandElement) obj;
				} else {
					tmp = null;
				}

			}
			tmp = parents.remove(0);
			browseCommand(tmp, parents);
		}
		return variables;
	}
}